//之前是随机生成，这次想优化下，保证初始状态合理性
//生成过程
//第一个开始随机，然后后面随机保证不重复，校验横竖随机情况，如果都不行，那么上一个退回
import _ from 'lodash'
import { ref, markRaw } from 'vue'
export class ExchangeRect {
  constructor(type, index) {
    this.type = ref(type)
    this.index = ref(index)
    this.col = 5
    this.row = 5
  }
  style() {
    let i = this.index.value
    let x = i % this.col
    let y = Math.floor(i / this.col)
    let w = 50
    let h = 50
    let left = x * 50
    let top = y * 50
    let style = {
      transform: `translate(${left}px,${top}px)`
    }
    return style
  }
  setValue(item) {
    this.index.value = item.index.value
    this.type.value = item.type.value
    return this
  }
  setType(type) {
    this.type.value = type
  }
  setIndex(index) {
    this.index.value = index
  }
}
export default class ExchangeMap {
  constructor() {
    this.stack = []
    this.col = 5
    this.row = 5
    this.num = this.col * this.row
    this.checkNum = 3
    this.types = 5
    this.init(0)
    this.isOver = false
  }
  init(index) {
    if (!this.isOver) {
      if (index == this.num) {
        //这里还要保证至少有一个解，否则还是要跳过
        if (this.checkHasResolve()) {
          //console.log(`有解了`)
          //console.log(`初始化结束`, _.chunk(this.stack, this.col))
          this.isOver = true
        }
        return 1
      }
      let enable_types = this.getEnableTypes(index)
      //console.log(enable_types)
      //这里enable_types 打乱次序
      enable_types = _.shuffle(enable_types)
      for (var i = 0; i < enable_types.length; i++) {
        let type = enable_types[i]
        this.stack.push(markRaw(new ExchangeRect(type, this.stack.length)))
        this.init(index + 1)
        if (this.isOver) break
        this.stack.pop()
      }
    }
  }
  //判断有解
  checkHasResolve() {
    let enable = false
    for (var i = 0; i < this.num; i++) {
      enable = this.checkResolve(i)
      if (enable) break
    }
    return enable
  }
  //校验有解
  checkResolve(index) {
    //保证行有解就行了
    let enable = this.checkRowResolve(index)
    if (!enable) enable = this.checkColResolve(index)
    return enable
  }
  //这里直接找到所有组合，不使用check方法
  getResolveList() {
    let rows = this.getRowsResolve()
    let cols = this.getColsResolve()

    let list = []
    list = list.concat(rows)
    list = list.concat(cols)

    return list
  }
  //消除最大的解，然后合并列，添加新的数字，然后重新判断是否有解
  //先把type设置为-1，然后打平，打平的操作是不断交换，如果上层为-1就交换这样可以逐个替换，最后将-1的值换成新的数字
  clearMaxResolve() {
    let list = this.getResolveList()
    let maxResolve
    if (list.length) {
      list.sort((a, b) => {
        return a.length - b.length
      })
      maxResolve = list.pop()
      //清空
      maxResolve.forEach((res) => {
        this.stack[res.index.value].setType(-1)
        this.addColumnNew(res.index.value)
      })
      this.format()
      //这里打平操作用交换不符合现实操作，应该是当前的删除，然后列末尾添加对应的属性，然后再逐个往前移动
      this.colPress()
      //这边重复校验
      //this.clearMaxResolve()
    }
  }
  format() {
    console.log(
      _.chunk(
        this.stack.map((res) => res.type.value),
        this.col
      )
    )
  }
  //添加列的新数据
  addColumnNew(index) {
    let type = Math.floor(Math.random() * this.types)
    this.stack[index + this.num] = markRaw(new ExchangeRect(type, index + this.num))
  }
  colPress() {
    for (var i = 0; i < this.stack.length; i++) {
      //只要管自己就行了
      if (this.stack[i] && this.stack[i].type.value == -1) {
        //找到最近的有数字的进行交换
        let next = i + this.col
        while (this.stack[next] && this.stack[next].type.value == -1) {
          next = next + this.col
        }
        if (this.stack[next]) {
          this.swap(next, i)
        }
      }
    }
    //最后清除-1栏目
  }
  //如果是-1，那么在同一列的所有数据都会向前移动一格，并且删除自己
  _colPress() {
    for (var i = this.num - 1; i >= 0; i--) {
      let j = i
      while (this.stack[j] && this.stack[j].type.value == -1 && j > -1 && j < this.num - this.col) {
        this.swap(j, j + this.col)
        //这里直接设置新的数字，因为还要参与后面的移动
        this.assignNew(j + this.col)
        j = j - this.col
      }
    }
  }
  //获取所有块级，连续2个，3个，自动拆分
  getRowsChunk() {
    let rows = this.getRows()
    let chunkNum = this.checkNum
    let rows_chunks = []

    rows.forEach((row) => {
      rows_chunks = rows_chunks.concat(this.getListChunk(row, chunkNum, this.col))
    })

    return rows_chunks
  }
  getColsChunk() {
    let cols = this.getCols()
    let chunkNum = this.checkNum
    let cols_chunks = []

    cols.forEach((col) => {
      cols_chunks = cols_chunks.concat(this.getListChunk(col, chunkNum, this.row))
    })

    return cols_chunks
  }
  getListChunk(list, min, max) {
    let len = list.length
    let chunks = []
    max = max || list.length
    for (var chunkNum = min; chunkNum <= max; chunkNum++) {
      for (var i = 0; i <= len - chunkNum; i++) {
        let chunk = []
        for (var j = 0; j < chunkNum; j++) {
          let index = i + j
          chunk.push(list[index])
        }
        chunks.push(chunk)
      }
    }
    return chunks
  }
  getRows() {
    let rows = []
    for (var row = 0; row < this.row; row++) {
      let list = []
      for (var i = 0; i < this.col; i++) {
        let index = row * this.col + i
        list.push(this.stack[index])
      }
      rows.push(list)
    }
    return rows
  }
  getCols() {
    //这里可以矩阵颠倒
    let cols = []
    for (var col = 0; col < this.col; col++) {
      let list = []
      for (var i = 0; i < this.row; i++) {
        let index = i * this.col + col
        list.push(this.stack[index])
      }
      cols.push(list)
    }
    return cols
  }
  //获取行有解项目
  getRowsResolve() {
    let rows_chunks = this.getRowsChunk()
    let rows_chunks_isSame = []
    rows_chunks.forEach((chunk) => {
      let isSame = chunk.every((res) => res.type.value == chunk[0].type.value)
      if (isSame) {
        rows_chunks_isSame.push(chunk)
      }
    })
    return rows_chunks_isSame
  }
  //获取列有解项目
  getColsResolve() {
    let cols_chunks = this.getColsChunk()
    let cols_chunks_isSame = []
    cols_chunks.forEach((chunk) => {
      let isSame = chunk.every((res) => res.type.value == chunk[0].type.value)
      if (isSame) {
        cols_chunks_isSame.push(chunk)
      }
    })
    return cols_chunks_isSame
  }
  //重新生成数字，然后校验，然后再次生成
  resolveNew(resolveList) {}
  //校验行有解 废弃，用新的方法
  checkRowResolve(index) {
    let enable = false
    let x = Math.floor(index / this.col)
    let y = Math.floor(index % this.col)
    if (y > 0) {
      //左边交换
      this.swap(index - 1, index)
      enable = this.checkOneRowResolve(x)
      this.swap(index - 1, index)
    }
    if (!enable && y < this.col - 1) {
      //右边交换
      this.swap(index, index + 1)
      enable = this.checkOneRowResolve(x)
      this.swap(index, index + 1)
    }
    return enable
  }
  checkColResolve(index) {
    let enable = false
    let x = Math.floor(index / this.col)
    let y = Math.floor(index % this.col)
    if (x > 0) {
      //上边交换
      this.swap(index, index - this.col)
      enable = this.checkOneColResolve(y)
      this.swap(index, index - this.col)
    }
    if (!enable && x < this.row - 1) {
      //下边交换
      this.swap(index, index + this.col)
      enable = this.checkOneColResolve(y)
      this.swap(index, index + this.col)
    }
    return enable
  }
  //校验这一列有没有可能性
  checkOneColResolve(col) {
    let enable = false
    for (var i = 0; i <= this.row - this.checkNum; i++) {
      let arr = []
      for (var j = 0; j < this.checkNum; j++) {
        let index = col + (i + j) * this.col
        arr.push(this.stack[index])
      }

      if (arr.every((res) => res.type == arr[0].type)) {
        enable = true
        break
      }
    }
    return enable
  }
  //校验这一行有没有可能性
  checkOneRowResolve(row) {
    let enable = false
    for (var i = 0; i <= this.col - this.checkNum; i++) {
      let arr = []
      for (var j = 0; j < this.checkNum; j++) {
        let index = row * this.col + (i + j)
        arr.push(this.stack[index].type.value)
      }

      if (arr.every((res) => res.type == arr[0].type)) {
        enable = true
        break
      }
    }
    return enable
  }
  swap(a, b) {
    let temp = this.stack[a]
    this.stack[a] = this.stack[b]
    this.stack[a].setIndex(a)
    this.stack[b] = temp
    this.stack[b].setIndex(b)
  }
  //为了拓展后面新添加的也要无解，这里要增加判断
  checkRow(index, type) {
    let { checkNum } = this
    //位置在自身，前面几个
    let y = Math.floor(index % this.col)

    let s = y - checkNum + 1
    let e = y
    let row = []

    if (s >= 0) {
      //自己不算
      for (var i = 1; i < checkNum; i++) {
        let stack_type = this.stack[index - i].type.value
        row.push(stack_type)
      }
      return !row.every((res) => res == type)
    }
    return true
  }
  checkCol(index, type) {
    let { checkNum } = this
    let x = Math.floor(index / this.col)

    let s = x - checkNum + 1
    let e = x
    let col = []

    if (s >= 0) {
      for (var i = 1; i < checkNum; i++) {
        let stack_type = this.stack[index - this.col * i].type.value
        col.push(stack_type)
      }
      return !col.every((res) => res == type)
    }
    return true
  }
  getEnableTypes(index) {
    let { types } = this
    let enable_types = []
    for (var i = 0; i < types; i++) {
      let type = i
      if (this.checkRow(index, i) && this.checkCol(index, i)) {
        enable_types.push(i)
      }
    }
    return enable_types
  }
}

//对逻辑进行校验
export class ExchangeMapUnit {
  constructor() {
    //this.testColResolve()
    //this.testRowResolve()
    //this.testRowsCols()
    //this.testListChunk()
    //this.testRowsChunk()
    //this.testColsChunk()
    //this.testRowsResolve()
    //this.testColsResolve()
    //this.test_getResolveList()
    this.test_clearMaxResolve()
    ////this.test_swap()
  }
  //校验列有解
  testColResolve() {
    console.log(`校验列有解开始`)
    let em = new ExchangeMap()
    em.stack[0].setType(1)
    em.stack[5].setType(1)
    em.stack[10].setType(1)

    let result = em.checkColResolve(0)
    if (result) {
      console.log(`校验列有解成功`)
    } else {
      console.log(`校验列有解失败`)
    }
  }
  //校验行有解
  testRowResolve() {
    console.log(`校验行有解开始`)
    let em = new ExchangeMap()
    em.stack[0].setType(1)
    em.stack[1].setType(1)
    em.stack[2].setType(1)

    let result = em.checkRowResolve(0)
    if (result) {
      console.log(`校验行有解成功`)
    } else {
      console.log(`校验行有解失败`)
    }
  }
  //拆分成行列数组
  testRowsCols() {
    console.log(`校验行列拆分开始`)
    let em = new ExchangeMap()

    let rows = em.getRows()
    let cols = em.getCols()

    console.log('stack', this.stackFormat(em))
    console.log('rows', rows)
    console.log('cols', cols)
  }
  testListChunk() {
    console.log(`校验一行拆成chunks`)
    let em = new ExchangeMap()
    let chunks = em.getListChunk([1, 2, 3, 4, 5], 3, 5)
    console.log('chunks', chunks)
  }
  testRowsChunk() {
    console.log(`校验矩阵行拆分固定长度的块级`)
    let em = new ExchangeMap()
    let chunks = em.getRowsChunk()

    console.log(`stack`, this.stackFormat(em))
    console.log(`chunks`, chunks)
  }
  testColsChunk() {
    console.log(`校验矩阵列拆分固定长度的块级`)
    let em = new ExchangeMap()
    let chunks = em.getColsChunk()

    console.log(`stack`, this.stackFormat(em))
    console.log(`chunks`, chunks)
  }
  testRowsResolve() {
    console.log(`获取行有解的项目`)
    let em = new ExchangeMap()
    em.stack[0].setType(1)
    em.stack[1].setType(1)
    em.stack[2].setType(1)
    let chunks = em.getRowsResolve()

    console.log(`stack`, this.stackFormat(em))
    console.log(`chunks`, chunks)
  }
  testColsResolve() {
    console.log(`获取列有解的项目`)
    let em = new ExchangeMap()
    em.stack[0].setType(1)
    em.stack[5].setType(1)
    em.stack[10].setType(1)
    let chunks = em.getColsResolve()

    console.log(`stack`, this.stackFormat(em))
    console.log(`chunks`, chunks)
  }
  test_getResolveList() {
    console.log(`获取所有有解的项目-行列`)
    let em = new ExchangeMap()
    em.stack[0].setType(1)
    em.stack[5].setType(1)
    em.stack[10].setType(1)
    em.stack[15].setType(1)
    let chunks = em.getResolveList()
    console.log(`stack`, this.stackFormat(em))
    console.log(`chunks`, chunks)
  }
  test_clearMaxResolve() {
    console.log(`清空最大长度的解`)
    let em = new ExchangeMap()
    em.stack[0].setType(1)
    em.stack[5].setType(1)
    em.stack[10].setType(1)
    em.stack[15].setType(1)
    let chunks = em.clearMaxResolve()
    console.log(`stack`, this.stackFormat(em))
    console.log(`chunks`, chunks)
  }
  test_swap() {
    console.log(`交换校验`)
    let em = new ExchangeMap()
    console.log(em.stack[0].index.value, em.stack[1].index.value)
    console.log(this.stackFormat(em))
    em.swap(0, 1)
    console.log(em.stack[0].index.value, em.stack[1].index.value)
    console.log(this.stackFormat(em))
  }
  stackFormat(em) {
    return _.chunk(
      em.stack.map((res) => res.type.value),
      em.col
    )
  }
}
